#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

from dws.commons.common import log, dws_exec_cmd_by_type
from dws.commons.const import IntrusiveMode
from dws.commons.function_tool import log_start
from dws.resource.dws_common import DwsCommon

BALANCED_POS = 1
CLUSTER_STATE_POS = 1
MIN_RESULT_COUNT = 3
COORDINATOR_IP_POS = 2


class DwsCluster:

    def __init__(self, user_name, env_file, pid=""):
        self._original_cluster_lines = []  # 保存查询结果的原始字符串
        self._original_cluster_detail_lines = []
        self._user_name = user_name
        self._env_file = env_file
        self._pid = pid
        self._node_info = {
            "endpoint": "",
            "extendInfo": "",
            "name": "",
            "role": IntrusiveMode.INVALID_INTRUSIVE_MODE,
            "subType": "DWS-cluster",
            "type": "",
            "id": ""
        }
        self._query_success = False
        self._query_detail_success = False
        self._exec_query_status()
        self._exec_query_status_detail()

    @staticmethod
    @log_start()
    def get_all_cluster_node_name(usr_name, env_path) -> set:
        """
        功能：获取集群所有节点主机名
        参数：
            usr_name：用户名
            env_path境变量文件路径
        返回值：
            成功：集群节点主机名的set
            失败：空set
        """
        return set(DwsCommon(usr_name, env_path).filter_cluster_node_info("nodeName"))

    @staticmethod
    @log_start()
    def get_all_cluster_node_ip(usr_name, env_path) -> set:
        """
        功能：获取集群所有节点ip
        参数：
            usr_name： 用户名
            env_path：境变量文件路径
        返回值：
            成功; 集群节点ip的set
            失败：空set
        """
        return set(DwsCommon(usr_name, env_path).filter_cluster_node_info("sshChannel"))

    def is_balanced(self):
        """
        从字符串中筛选出当前集群的平衡状态, 如果是yes返回True, 否则返回False
        """
        if not self._query_success:
            log.error(f"Query state is false, PID: {self._pid}.")
            return False
        balanced_str = ""
        for per_line in self._original_cluster_lines:
            if "balanced" in per_line and ":" in per_line:
                balanced_str = per_line.split(":")[BALANCED_POS].strip()
                log.info(f"Find cluster balanced success, balanced_str: {balanced_str}, PID: {self._pid}.")
                break

        return balanced_str.lower() == "yes"

    def get_cluster_state(self):
        """
        从字符串中筛选出集群状态
        """
        if not self._query_success:
            log.error(f"Query state is false, PID: {self._pid}.")
            return "Unstarted"  # 查询出错统一标识为未启动状态
        for per_line in self._original_cluster_lines:
            if "cluster_state" in per_line and ":" in per_line:
                log.info(f"Find cluster_state success, PID: {self._pid}.")
                return per_line.split(":")[CLUSTER_STATE_POS].strip()

        log.info(f"Find cluster_state failed, PID: {self._pid}.")
        return ""

    def is_disk_damaged(self):
        """
        从字符串中判断是否Disk damaged
        """
        if not self._query_detail_success:
            log.error(f"Query state datail is false, PID: {self._pid}.")
            return True  # 查询出错统一标识为未启动状态
        for per_line in self._original_cluster_detail_lines:
            if "Disk damaged" in per_line:
                log.info(f"Disk damaged, PID: {self._pid}.")
                return True

        log.info(f"Disk not damaged, PID: {self._pid}.")
        return False

    @log_start()
    def get_min_cn_ip(self):
        """
        从所有CN节点的IP中筛选出最小的IP作为集群的唯一标识
        """
        min_cn_ip = ""
        all_cn_ip = DwsCommon(self._user_name, self._env_file).get_all_cn_hostip()
        if not all_cn_ip:
            log.error("Get all cn hostip failed.")
            return min_cn_ip

        for tmp_ip in all_cn_ip:
            if not min_cn_ip:
                min_cn_ip = tmp_ip
                continue
            try:
                tmp_ip_list = [int(x) for x in tmp_ip.split(".")]
            except Exception:
                log.warn(f"Invalid tmp ip [{tmp_ip}].")
                continue
            try:
                min_ip_list = [int(x) for x in min_cn_ip.split(".")]
            except Exception:
                log.warn(f"Invalid min ip [{min_cn_ip}].")
                continue
            if tmp_ip_list < min_ip_list:
                min_cn_ip = tmp_ip
        return min_cn_ip

    @log_start()
    def get_cluster_version(self):
        """
        获取集群版本
        """
        ret, out_info = dws_exec_cmd_by_type(self._user_name, self._env_file, "cm_ctl --version")
        if not ret:
            log.error(f"Query cluster version error whith return: {out_info}, PID: {self._pid}.")
            return ""
        log.info(f"Version: {out_info}, PID: {self._pid}.")
        out_info = out_info.split('\n')
        version_str = ""
        for each_line in out_info:
            if "build" in each_line and "GaussDB" in each_line:
                version_str = each_line.lower()
                break
        if not version_str:
            log.error("Fail to get version")
            return ""
        version_infos = version_str.split(" ")
        try:
            version_index = version_infos.index("build")
        except Exception as ex:
            log.error(f"Get version info failed ex: {ex}")
            return ""
        version = version_infos[version_index - 1]
        # 返回版本时去掉小版本，比如8.0.0.3返回8.0.0
        return ".".join(version.strip().split(".")[:3])

    def get_node_info(self):
        """
        获取当前节点信息
        """
        ret_node_array = []
        common_obj = DwsCommon(self._user_name, self._env_file, self._pid)
        self._node_info["name"] = common_obj.get_hostname()
        self._node_info["extendInfo"] = {
            "cluster_version": self.get_cluster_version(),
            "coordinator_ip": self.get_min_cn_ip(),
            "cluster_state": self.get_cluster_state()
        }
        self._node_info["role"] = IntrusiveMode.INTRUSIVE_MODE
        ret_node_array.append(self._node_info)
        return ret_node_array

    def _exec_query_status(self):
        """
        执行命令查询集群详细信息
        """
        self._query_success, self._original_cluster_lines = DwsCommon(self._user_name,
                                                                      self._env_file).exec_query_status()

    def _exec_query_status_detail(self):
        """
        执行命令查询集群详细信息
        """
        self._query_detail_success, self._original_cluster_detail_lines \
            = DwsCommon(self._user_name, self._env_file).exec_query_status_detail()
